﻿using InfoPathHelpers.Internal;
using System;
using System.IO;
using System.Security;
using System.Security.Principal;

namespace InfoPathHelpers.Cryptography.Authentication
{
    public static class IdGenerator
    {
        /// <summary>
        /// Gets the WindowsId for the current user.
        /// </summary>
        /// <returns>A string representation of the WindowsId for the current user.</returns>
        public static string GetWindowsId()
        {
            try
            {
                return WindowsIdentity.GetCurrent().Name;
            }
            catch (SecurityException)
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Gets an Id using as many of the parts requested as possible.
        /// When the method completed the identifiers parameter will contain only those IdParts which could be used.
        /// </summary>
        /// <param name="identifers"></param>
        /// <returns>All of the IdParts which could be discovered concatenated into a single string.</returns>
        public static string GetUniqueId(ref IdParts identifers)
        {
            string UniqueId = string.Empty;

            foreach (IdParts IdPart in Enum.GetValues(typeof(IdParts)))
            {
                if (!(IdPart == IdParts.None || IdPart == IdParts.All) && (identifers & IdPart) == IdPart)
                {
                    string Part = GetPart(IdPart);
                    if (String.IsNullOrEmpty(Part))
                    {
                        identifers &= ~IdPart;
                    }
                    else
                    {
                        UniqueId += Part;
                    }
                }
            }

            return UniqueId;
        }

        private static string GetPart(IdParts IdPart)
        {
            switch (IdPart)
            {
                case IdParts.ComputerName:
                    return GetComputerName();
                case IdParts.MotherboardId:
                    return GetMotherboardId();
                case IdParts.ProcessorId:
                    return GetProcessorId();
                case IdParts.ProductId:
                    return GetProductId();
                case IdParts.VolumeSerialNo:
                    return GetVolumeSerialNumber();
                default:
                    return string.Empty;
            }
        }

        private static string GetComputerName()
        {
            try
            {
                return Environment.MachineName;
            }
            catch (InvalidOperationException)
            {
                return string.Empty;
            }
        }

        private static string GetMotherboardId()
        {
            return WMIHelper.GetProperty("Win32_BaseBoard", "SerialNumber");
        }

        private static string GetProcessorId()
        {
            return WMIHelper.GetProperty("Win32_Processor", "ProcessorId");
        }

        private static string GetProductId()
        {
            return WMIHelper.GetProperty("Win32_OperatingSystem", "SerialNumber");
        }

        private static string GetVolumeSerialNumber()
        {
            string OperatingSystemVolume = Path.GetPathRoot(Environment.SystemDirectory);
            return WMIHelper.GetProperty("Win32_LogicalDisk.DeviceID=\"C:\"", "VolumeSerialNumber");
        }
    }

    [Flags]
    public enum IdParts
    {
        None = 0 << 0,
        ComputerName = 1 << 0,
        MotherboardId = 1 << 1,
        ProcessorId = 1 << 2,
        ProductId = 1 << 3,
        VolumeSerialNo = 1 << 4,
        All = ComputerName | MotherboardId | ProcessorId | ProductId | VolumeSerialNo
    }
}
